
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <asm/io.h>

#include "mpu6050.h"


/* mpu6050 apis */

static int mpu6050_init(void)
{
    u8 mpu_addr = 0;
    struct i2c_client *client = mpudev.private_data;

    /* 配置 i2c */
    iic_write_reg(&mpudev, PWR_MGMT_1, 0x80); // 复位
    mdelay(100);
    iic_write_reg(&mpudev, PWR_MGMT_1, 0x00); // 关闭睡眠模式
    iic_write_reg(&mpudev, SMPLRT_DIV, 0X07);
    iic_write_reg(&mpudev, CONFIG, 0X06);
    iic_write_reg(&mpudev, GYRO_CONFIG, GYRO_SCALE); // 陀螺仪量程，±2000°/s
    iic_write_reg(&mpudev, ACCEL_CONFIG, ACCEL_SCALE); // 加速度计量程，±2g

    mpu_addr = iic_read_reg(&mpudev, WHO_AM_I);
    if (mpu_addr != client->addr)
    {
        printk("mpu6050 initial failed! (%#x->%#x)\n", client->addr, mpu_addr);
        return -1;
    }

    return 0;
}

static short mpu6050_getData(int data_type)
{
    u8 data_H = 0;
    u8 data_L = 0;
    short ret = 0;

    data_H = iic_read_reg(&mpudev, data_type >> 8);
    data_L = iic_read_reg(&mpudev, data_type & 0xFF);
    ret = (data_H << 8) + data_L;

    return ret;
}

/* 字符设备读写，用于上层调用 */
static int mpu6050_open(struct inode *inode, struct file *filp)
{
#ifdef ATOMIC
    if (!atomic_dec_and_test(&mpudev.lock))
    {
        atomic_inc(&mpudev.lock);
        return -EBUSY;
    }
#endif

#ifdef TIMER
    mpudev.period = 1000;
#endif

    filp->private_data = &mpudev; /* 设置私有数据 */
    return mpu6050_init();
}

static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret;
    short mpu6050_ret[8] = {0};

    mpu6050_ret[0] = mpu6050_getData(ACCEL_XOUT);
    mpu6050_ret[1] = mpu6050_getData(ACCEL_YOUT);
    mpu6050_ret[2] = mpu6050_getData(ACCEL_ZOUT);
    mpu6050_ret[3] = mpu6050_getData(GYRO_XOUT);
    mpu6050_ret[4] = mpu6050_getData(GYRO_YOUT);
    mpu6050_ret[5] = mpu6050_getData(GYRO_ZOUT);
    mpu6050_ret[6] = mpu6050_getData(TEMPERATURE);
    
    /* 将读取得到的数据拷贝到用户空间*/
    ret = copy_to_user(buf, mpu6050_ret, cnt);
    if (ret != 0)
    {
        printk("copy_to_user error!");
        return -1;
    }

    return 0;
}

static int mpu6050_release(struct inode *inode, struct file *filp)
{
    /*struct iic_dev *dev = filp->private_data;*/
#ifdef ATOMIC
    atomic_inc(&dev->lock);
#endif

    return 0;
}

static struct file_operations mpu6050_fops = {
    .owner = THIS_MODULE,
    .open = mpu6050_open,
    .read = mpu6050_read,
    .release = mpu6050_release,
};

/* i2c设备的具体功能 */
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk(KERN_EMERG "\t match successed, initial iic charactor device\r\n");

    /* 注册字符设备驱动 */
    /* 1、创建设备号 */
    if (mpudev.major)
    {
        mpudev.devid = MKDEV(mpudev.major, 0);
        register_chrdev_region(mpudev.devid, DEV_CNT, CHRDEV_NAME);
    }
    else
    {
        alloc_chrdev_region(&mpudev.devid, 0, DEV_CNT, CHRDEV_NAME); /* 申请设备号 */
        mpudev.major = MAJOR(mpudev.devid); /* 获取主设备号 */
        mpudev.minor = MINOR(mpudev.devid); /* 获取次设备号 */
    }

    printk("mpudev major=%d,minor=%d\r\n",mpudev.major, mpudev.minor);

    /* 2、初始化 cdev */
    mpudev.cdev.owner = THIS_MODULE;
    cdev_init(&mpudev.cdev, &mpu6050_fops);

    /* 3、添加一个 cdev */
    cdev_add(&mpudev.cdev, mpudev.devid, DEV_CNT);

    /* 4、创建类 */
    mpudev.class = class_create(THIS_MODULE, CHRDEV_NAME);
    if (IS_ERR(mpudev.class))
    {
        return PTR_ERR(mpudev.class);
    }

    /* 5、创建设备 */
    mpudev.device = device_create(mpudev.class, NULL, mpudev.devid, NULL, CHRDEV_NAME);
    if (IS_ERR(mpudev.device))
    {
        return PTR_ERR(mpudev.device);
    }

    mpudev.private_data = client;

#ifdef TIMER
    /* 初始化定时器 */
    init_timer(&mpudev.timer);
    mpudev.timer.function = timer_handle;
#endif

    return 0;
}

static int mpu6050_remove(struct platform_device *dev)
{
#ifdef TIMER
    /* 删除定时器 */
    del_timer_sync(&mpudev.timer);
#endif

    /* 注销字符设备 */
    cdev_del(&mpudev.cdev);/* 删除 cdev */
    unregister_chrdev_region(mpudev.devid, DEV_CNT);

    device_destroy(mpudev.class, mpudev.devid);
    class_destroy(mpudev.class);

    return 0;
}

static int __init iic_mpu6050_init(void)
{
    int ret;
    printk("mpu6050 drivers init\r\n");

    ret = i2c_add_driver(&mpu6050_driver);

    return ret;
}

static void __exit iic_mpu6050_exit(void)
{
    printk("mpu6050 drivers exit\r\n");

    i2c_del_driver(&mpu6050_driver);
}

module_init(iic_mpu6050_init);
module_exit(iic_mpu6050_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xue");


